﻿using System;
using System.Collections.Generic;
using Microsoft.Popfly.GameCreator.Data;

namespace Microsoft.Popfly.GameCreator.GameEngine.Behaviors
{
    public static class PropertyBehaviorFactory
    {
        public static IBehavior CreatePropertyBehavior(Game game, BehaviorOwner owner, Data.BehaviorInfo behaviorInfo)
        {
            Type behaviorType = behaviorInfo.GetType();
            if (behaviorType != typeof(Data.PropertyChangeBehaviorInfo))
            {
                throw new ArgumentException(
                    Utils.ErrorMessagePrefix +
                    "PropertyChangeBehaviors must be created with a Data.PropertyChangeBehaviorInfo.  A '" +
                    behaviorType.ToString() +
                    "' was passed in."
                    );
            }
            Data.PropertyChangeBehaviorInfo propBehavior = (Data.PropertyChangeBehaviorInfo)behaviorInfo;

            PropertyHolder holder = null;
            string actorClass = null;

            switch (propBehavior.ItemReference.Type)
            {
                case "self":
                    holder = owner;
                    break;
                case "game":
                    holder = game;
                    break;
                case "scene":
                    {
                        Dictionary<String, Data.IncludeObjectInfo>.Enumerator e = propBehavior.ItemReference.Includes.GetEnumerator();
                        bool eret = e.MoveNext();
                        System.Diagnostics.Debug.Assert(eret);




                        if (e.Current.Key != owner.Scene.Name)
                        {
                            throw new ArgumentException(
                                "Property Change Behaviors can only affect the current scene.  \n" + 
                                "CurrentScene: '" + owner.Scene.Name + "'.\n" + 
                                "Property Change target: '" + e.Current.Key + "'."
                            );
                        }
                        holder = owner.Scene;
                        System.Diagnostics.Debug.Assert(!e.MoveNext());
                    }
                    break;
                case "actorInstance":
                    {
                        Dictionary<String, Data.IncludeObjectInfo>.Enumerator e = propBehavior.ItemReference.Includes.GetEnumerator();
                        bool eret = e.MoveNext();
                        System.Diagnostics.Debug.Assert(eret);

                        holder = owner.Scene.GetActor(e.Current.Key);
                        System.Diagnostics.Debug.Assert(!e.MoveNext());
                    }
                    break;
                case "actor":
                    {
                        Dictionary<String, Data.IncludeObjectInfo>.Enumerator e = propBehavior.ItemReference.Includes.GetEnumerator();
                        bool eret = e.MoveNext();
                        System.Diagnostics.Debug.Assert(eret);

                        actorClass = e.Current.Key;
                    }
                    break;
                default:
                    throw new ArgumentException(
                        Utils.ErrorMessagePrefix +
                        "PropertyAddBehaviors don't know how to handle item references of type '" +
                        propBehavior.ItemReference.Type +
                        "'.");
            }

            if (holder == null && actorClass == null)
            {
                Dictionary<String, Data.IncludeObjectInfo>.Enumerator e = propBehavior.ItemReference.Includes.GetEnumerator();
                bool eret = e.MoveNext();
                System.Diagnostics.Debug.Assert(eret);

                throw new ArgumentException(
                    Utils.ErrorMessagePrefix +
                    "Couldn't find a scene, actor or actor instance named '" +
                    e.Current.Key +
                    "'.");
            }

            switch (propBehavior.ChangeType)
            {
                case PropertyChangeBehaviorInfo.PropertyChangeType.ADDVALUE :
                    if (holder != null)
                        return new PropertyAddBehavior(holder, propBehavior.PropertyName, Double.Parse(propBehavior.AddValue));
                    else
                        return new PropertyAddsBehavior(game, owner, actorClass, propBehavior.PropertyName, Double.Parse(propBehavior.AddValue));
                case PropertyChangeBehaviorInfo.PropertyChangeType.SETVALUE :
                    if (holder != null)
                        return new PropertySetBehavior(holder, propBehavior.PropertyName, propBehavior.SetValue);
                    else
                        return new PropertySetsBehavior(game, owner, actorClass, propBehavior.PropertyName, propBehavior.SetValue);
                default :
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to create a property behavior with a change type of '" +
                        propBehavior.ChangeType.ToString() +
                        "'.");
            }
        }
    }
}
